前端pdf文件预览以及大文件处理

您所在的位置:网站首页 lg velvet双卡 前端pdf文件预览以及大文件处理

前端pdf文件预览以及大文件处理

#前端pdf文件预览以及大文件处理| 来源: 网络整理| 查看: 265

前言

一般我们在web中展示pdf文件,使用的都是mozilla/pdf.js这个开源库,它可以帮我们处理服务端返回文件流,解析成pdf图像(可选择canvas或者svg),再插入到页面中展示。如果使用react的话,可以选用react-pdf,react-pdf是基于pdf.js的react封装,是一个较成熟的pdf预览开源库。使用pdf.js或者react-pdf,基本可以解决大部分的pdf文件预览场景。

问题

在项目的实际开发中,我们遇到了一些问题:

大文件的pdf(比如超过100MB),pdf.js会下载完成整个文件后再开始渲染,导致等待时间较长。

在钉钉Webview中打开的H5项目,使用pdf预览功能,发现部分pdf文件无法展示,直接白屏。

以上问题经过排查后,得出了以下的结论:

pdf.js 实际上是支持分片加载的,即不用下载完成整个文件,优先展示当前的页码,可以避免下载文件完成后再展示的问题。但是开启这个功能,需要服务端的支持。pdf.js中的代码实现片段

原因是因为钉钉的Webview不支持Response这个浏览器API,而pdf.js部分逻辑使用到了Response这个特性,好在这个功能是在后续版本添加的,我们可以暂时使用老旧一点的版本(如1.6.210),但风险是,该版本后续修复的缺陷或者新特性,旧版本都不包含。MDN Response

文件分片加载

所谓的文件分片加载,实际上是通过HTTP-Range响应头来实现的,只要服务端实现了该响应头的支持,pdf.js就会自动开启文件分片,分片拉取pdf文件。这里来说一下整体的流程,以及需要注意的点

首先,pdf.js会发起第一个请求,服务端在返回的信息中告诉pdf.js,这个文件支持分片。

image.png 服务端需要返回以下响应头:

Accept-Ranges: bytes

告诉客户端,该文件是支持分片下载的

Access-Control-Expose-Headers: Accept-Ranges,Content-Range

告诉浏览器,ajax可以获取这两个响应头Accept-Ranges,Content-Range,在默认情况下,浏览器是不会开放所有响应头的读取的,这会导致pdf.js无法读到这两个响应头,所以需要加上这个,这个坑也是我踩到了的。

Content-Encoding: identity

告诉客户端,这个返回的文件流未经过编码。这个也是必要的,如果返回的是gzip,pdf.js是不会开启分片下载的。

Content-Length: 408244

告诉客户端,这个文件的字节总长度。

此次请求,只是为了确定文件的总长度(用于后面分片的计算)以及是否支持分片。

至此,pdf.js知道了该文件是支持分片下载的,同时也获取到了整个文件的字节长度,它会自动启用分片的策略,去计算每次个分片的大小,再次发起后续的请求。

比如,第二次请求:

pdf.js会在HTTP请求头中加上:

Accept-Encoding: identity 标识接受的文件流是未编码的 Range: bytes=0-65535 表示此次请求的是0-65535字节范围内的数据.

image.png

服务端的响应:

Content-Length: 65536 此次返回的总字节长度是65536bytes

Content-Range: bytes 0-65535/408244 表示此次返回的body内容是0-65535范围段的字节流

同时,后端在响应体中,带上字节流返回。

状态码标识为206,表示范围请求。

后续的请求

后续的请求都是类似的,都是pdf.js在请求头Range中标识此次请求的范围,服务端拿到Range后,进行处理,返回对应的文件流,同时,在Content-Length,Content-Range上也加上此次的请求的范围长度、开始、结束的字节。

image.png

以上,便是文件分片的整体逻辑,只要后端实现了对Ranges的处理,并符合HTTP的规范以及pdf.js的要求,就能开启pdf文件分片。

虚拟列表

在我测试的例子中,100MB的pdf文件,可能有306页,整个306页同时展示出来,体验效果会急速下降。在官方的issue中,有人提议加入虚拟滚动的支持,见issue。我这里是修改了react-pdf的源码,可以方便地将虚拟列表加入进来,这里使用的是react-windo。

预读取

pdf.js有个预读取的功能,哪怕你启用了文件分片的支持,它也会一片片地去帮我们把整个pdf文件提前下载下来,即使你没有翻页到后面的页码,这在移动端上会产生大量的流量消耗了,实际上我们可以关闭这个功能,只请求展示页码的文件流,避免整个文件下载下来。

当然,这样会产生一个问题,即快速翻到后面的页码时,页面白屏会稍久一点,因为没有预读取,需要等待重新等待请求返回后才能展示页面。

关闭pdf.js的预读取功能:

// 禁止 PDF.js 预读取 window.PDFJS.disableAutoFetch =true; 复制代码 总结

为了解决pdf大文件无法预览,以及在钉钉webview中,无法预览pdf的问题。我的解决方案是,修改了react-pdf的部分源码,使其基于pdf.js(1.6.210)版本,同时,让后端开启对文件分片的支持。前端加入虚拟滚动,提高大文件的体验,以及关闭了预读取,避免pdf提前读取整个文件。

但是HTTP-RANGE是一个通用的标准了,主要用与分片下载,不仅仅是pdf文件,任何文件都是可以支持的,关键是看前端如何处理的,在这里,是pdf.js帮我们处理了。

代码

Nodejs实现HTTP-Ranges的例子

React-pdf改造以及增加虚拟列表



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3